/*
 *  Entity.h
 *  IncrementalAlgorithmSerial
 *
 *  Created by Roman Kutlak on 29/03/2010.
 *  Copyright 2010 Roman Kutlak. All rights reserved.
 *
 */

#ifndef _ENTITY_H_
#define _ENTITY_H_
            
//#define ENTITY_DEBUG 1 // enable Entity debugging output
#include <iosfwd>            
#include <string>
#include <memory>
#include <vector>
#include <unordered_set>

#include "Property.h"

namespace kutlak {
namespace roman {
namespace gre {
    
    
    class Entity;
    //! \typedef declaration of a smart pointer to Entity
    typedef std::shared_ptr<Entity> EntityPtrT;
    
    //! Entity describes an object or person in the knowledge base.
    /*! The class Entity serves as a container for properties applicable to 
     *  a particular object. The class also carries the name of the object
     *  and an Id number.
     */
    class Entity {    
    public:
        // to allow for changing the container from say list to vector use typedef
        /*! \typedef ContainerT is the type of container the entity class uses to 
         *  store the properties. It could be a vector or a list or a similar ADT.
         */
        // unfortunately does not work...
        //        typedef  std::unordered_set<Property, PropertyHash, std::equal_to<Property>, Loki::SmallObjectAllocPolicy<Property, 64>> ContainerT; // 40s 296MB
        typedef  std::unordered_set<Property, PropertyHash> ContainerT; // 39s 326MB
        //! \typedef iterator type
        typedef  ContainerT::iterator iterator;
        //! \typedef const iterator type
        typedef  ContainerT::const_iterator const_iterator; 
        
#pragma mark -
#pragma mark lifecycle
        
        explicit Entity(const std::string& inName="Entity", int inId=-1);
        
        Entity(const Entity& src);
        
        Entity(Entity&& src);
        
        Entity& operator=(Entity rhs);
        
        ~Entity() noexcept(true) ;
        
        friend void swap(Entity& lhs, Entity& rhs) noexcept(true);
        
#pragma mark -
#pragma mark operators
        
        bool operator==(const Entity& rhs) const;
        bool operator!=(const Entity& rhs) const;
        
#pragma mark -
#pragma mark I/O
        //! Serialiser.
        friend std::ostream& operator<<(std::ostream& os, const Entity& obj);
        
        //! Deserialiser.
        friend std::istream& operator>>(std::istream& os, Entity& obj);
        
        void Serialise(std::ostream& os) const;
        
        void Deserialise(std::istream& is);
        
        /*! \brief This method prints the entity to os in RDF format.
         *
         *  The output of the formatter is 
         *  <name> <attribute> <value> .\n
         *  for each of the properties except for negations and inverse props.
         */
        std::ostream& ToRdf(std::ostream& os) const;
        
        //! String representation of this entity.
        std::string ToString() const;
        
#pragma mark -
#pragma mark accessors
        // do we really want to allow modifying the object from outside?
        // maybe we should provide const iterator only...
        
        //! Returns the iterator to the first property
        iterator        begin()        { return mProperties.begin(); }
        
        //! Returns the const iterator to the first property
        const_iterator  begin() const  { return mProperties.begin(); }
        
        //! Returns the iterator behind the last property
        iterator        end()          { return mProperties.end(); }
        
        //! Returns the const iterator behind the last property
        const_iterator  end()   const  { return mProperties.end(); }
        
        //! Returns Id.
        int Id() const;
        
        //! Returns the name of the entity.
        const std::string& Name() const;
        
        //! Returns the type of the entity.
        const Property& Type() const;
        
        //! Returns true if the property is either 'type' of a subclass of it.
        bool IsType(const Property& type) const;
        
        //! Returns a reference to the vector with properties.
        const ContainerT& Properties() const;
        
        //! Returns the number of properties.
        size_t Size() const;
        
        size_t Hash() const;
        
        //! Sets the Id.
        void SetId(const int id);
        
        //! Sets the name.
        void SetName(const std::string& name);
        
        //! Sets the type of the entity.
        void SetType(const Property& type);
        
        template <class T>
        void SetProperties(const T& inProperties) 
        {
            mProperties.clear();
            AddProperties(inProperties);
        }
        
        //! Clear the properties from the entity.
        void ClearProperties();
        
        /*! \brief Adds a property.
         *  The method also checks if the property being added is a type.
         *  If so, set the type of the Entity to given property.
         *  If ontology exists, while the property is being added,
         *  the type is set to the most specific type. E.g. if the current
         *  type is Person and the property being added is Musician and our
         *  ontology indicates that person subsumes musician, the current 
         *  type is set to musician. 
         *  If no ontology is loaded, the current type gets replaced by the
         *  new type. This means that if the knowledge base being read
         *  inserts types from least to most specific, mType will still be set
         *  to the most specific type.
         */
        void AddProperty(const Property& p);
        
        void AddProperty(Property&& p);        
        
        //! Adds properties from a container such as list or a vector.
        template <class T>
        void AddProperties(const T& inProperties) 
        {
            typename T::const_iterator it;
            for (it = inProperties.begin(); it != inProperties.end(); ++it) 
            {
                AddProperty(*it);
            }
        }
        
        //! Returns true if the entity has the given property.
        bool HasProperty(const Property& p) const;
        
        //! Returns true if the entity has the given attribute
        bool HasAttribute(const Vindex_t& p) const;
        
        //! Returns true if the entity has the given value
        bool HasValue(const Vindex_t& p) const;
        
        //! Checks if the entity has a property with given attribute and returns the corresponding value.
        /*! The method returns the DEFAULT_VALUE in case when the entity lacks the property.
         Also if more than one property has the given attribute, an arbitrary value from 
         the matching set will be returned.
         */
        Vindex_t ValueForAttribute(const Vindex_t& Attribute) const;
        
        //! Checks if the entity has a property with given attribute and returns the list of values.
        /*! The method returns all values corresponding to the given attribute.
         */
        std::vector<Vindex_t> ValuesForAttribute(const Vindex_t& Attribute) const;
        
    private:        
        void CreateHash();
        
#pragma mark -
#pragma mark data members
    private:
        size_t       mHash;
        int          mId;         //!< The id of the entity.
        std::string  mName;       //!< The name of the entity.
        Property     mType;       //!< The type of the entity (most specific)
        ContainerT   mProperties; //!< The collection of properties applicable to this entity.
    };
    
    /*! 
     \struct structure for comparing entity pointers
     This structure is used by containers such as std::map.
     */
    struct EntityPtrComp 
    {
        bool operator() (const EntityPtrT& lhs, const EntityPtrT& rhs) const 
        {
            return (lhs->Hash() < rhs->Hash()); 
        }
    };
    
    struct EntityPtrEqPred 
    {
        EntityPtrEqPred(const EntityPtrT& ref) : ref_(ref) {}
        bool operator() (const EntityPtrT& entity) const 
        {
            return ((*ref_) == (*entity)); 
        }
        EntityPtrT ref_;
    };
    
    struct EntityPtrNePred 
    {
        EntityPtrNePred(const EntityPtrT& ref) : ref_(ref) {}
        bool operator() (const EntityPtrT& entity) const 
        {
            return ((*ref_) != (*entity)); 
        }
        EntityPtrT ref_;
    };
    
    struct EntityHash 
    {
        size_t operator()(const Entity& e) 
        {
            return e.Hash();
        }
        
        bool operator()(const Entity& lhs, const Entity& rhs) 
        {
            return lhs.Hash() < rhs.Hash();
        }
    };
    
    struct EntityPtrHash 
    {
        size_t operator()(const EntityPtrT& e) 
        {
            return e->Hash();
        }
    };
    
} // namespace gre
} // namespace roman
} // namespace kutlak
#endif
